Created by miccall (转载请注明出处 miccall.tech)
基于 Unreal engine 4.20.2
1. EngineTypes.h
//452 line
UENUM()
enum EMaterialShadingModel
{
MSM_Unlit UMETA(DisplayName="Unlit"),
MSM_DefaultLit UMETA(DisplayName="Default Lit"),
MSM_Subsurface UMETA(DisplayName="Subsurface"),
MSM_PreintegratedSkin UMETA(DisplayName="Preintegrated Skin"),
MSM_ClearCoat UMETA(DisplayName="Clear Coat"),
MSM_SubsurfaceProfile UMETA(DisplayName="Subsurface Profile"),
MSM_TwoSidedFoliage UMETA(DisplayName="Two Sided Foliage"),
MSM_Hair UMETA(DisplayName="Hair"),
MSM_Cloth UMETA(DisplayName="Cloth"),
MSM_Eye UMETA(DisplayName="Eye"),
MSM_MAX,
};
此枚举确定在“材质编辑器”内的“shading model ”下拉列表中显示的内容
添加自己的 shading model :
MSM_Eye UMETA(DisplayName="Eye"),
MSM_StylizedShadow UMETA(DisplayName="Stylized Shadow"),
MSM_MAX,
2. Material.cpp
// 5621
bool UMaterial::IsPropertyActive(EMaterialProperty InProperty) const
UE 对 material 上的每个可能的 shading model 引脚 调用此函数 。
我们添加一个 Custonm 引脚 :
switch (InProperty)
{
// 。。。
case MP_CustomData0 :
Active = ShadingModel == MSM_ClearCoat || ShadingModel == MSM_Hair || ShadingModel == MSM_Cloth || ShadingModel == MSM_Eye || ShadingModel == MSM_StylizedShadow ;
// 。。。
}
此代码仅更改材质编辑器中的UI 。 Custom Data 0 和 Custom Data 1 都是单通道 float 属性 。
一旦我们修改了材质编辑器以便能够选择我们的新着色模型,我们需要确保我们的着色器知道它们何时被设置为使用我们的着色模型。
3. MaterialShared.cpp
// 1255
void FMaterial::SetupMaterialEnvironment(
EShaderPlatform Platform,
const FUniformExpressionSet& InUniformExpressionSet,
FShaderCompilerEnvironment& OutEnvironment
) const
此方法 允许你查看各种配置因素(例如 material 上的属性)。 OutEnvironment通过添加其他定义来修改变量
GetShadingModel() 并添加我们的 MSM_StylizedShadow 案例
//1445
switch(GetShadingModel())
{
...
case MSM_Eye: OutEnvironment.SetDefine(TEXT("MATERIAL_SHADINGMODEL_EYE"), TEXT("1")); break;
case MSM_StylizedShadow: OutEnvironment.SetDefine(TEXT("MATERIAL_SHADINGMODEL_STYLIZEDSHADOW"), TEXT("1")); break;
default:
...
};
当材质的Shading Model设置为MSM_StylizedShadowHLSL编译器时,将设置MATERIAL_SHADINGMODEL_STYLIZED_SHADOW为预处理器定义.
4. 更新 GBuffer着色模型 ID
通过MATERIAL_SHADINGMODEL_STYLIZED_SHADOW我们可以开始对着色器进行更改。我们需要做的第一件事是将新的着色模型ID写入GBuffer这允许DeferredLightPixelShader知道在运行光照计算时要尝试使用的着色模型。
DeferredShadingCommon.ush
//269
#define SHADINGMODELID_UNLIT 0
#define SHADINGMODELID_DEFAULT_LIT 1
. . .
#define SHADINGMODELID_EYE 9
#define SHADINGMODELID_STYLIZED_SHADOW 10
#define SHADINGMODELID_NUM 11
#define SHADINGMODELID_MASK 0xF
我们需要告诉着色器将此着色模型ID写入Gbuffer . 然后 ,
// 731
float3 GetShadingModelColor(uint ShadingModelID)
{
#if PS4_PROFILE
…
else if (ShadingModelID == SHADINGMODELID_EYE) return float3(0.3f, 1.0f, 1.0f);
else if (ShadingModelID == SHADINGMODELID_STYLIZED_SHADOW ) return float3(0.4f, 0.0f, 0.8f); // Purple
else return float3(1.0f, 1.0f, 1.0f); // White
#else
switch(ShadingModelID)
{
…
case SHADINGMODELID_EYE: return float3(0.3f, 1.0f, 1.0f);
case SHADINGMODELID_STYLIZED_SHADOW: return float3(0.4f, 0.0f, 0.8f); // Purple
default: return float3(1.0f, 1.0f, 1.0f); // White
}
#endif
}
现在我们需要告诉BasePassPixelShader将正确的ID写入Shading Model ID纹理.
ShadingModelsMaterial.ush
//11
void SetGBufferForShadingModel(
in out FGBufferData GBuffer,
in const FMaterialPixelParameters MaterialParameters,
const float Opacity,
const half3 BaseColor,
const half Metallic,
const half Specular,
const float Roughness,
const float3 SubsurfaceColor,
const float SubsurfaceProfile
)
此函数允许每个着色模型选择如何将各种PBR数据通道写入FGBufferData结构。
唯一需要做的就是确保分配了GBuffer.ShadingModelID。如果我们希望使用Custom Data 0材质编辑器中的通道,就可以在此处查询该值并将其写入GBuffer。
//128
#elif MATERIAL_SHADINGMODEL_EYE
GBuffer.ShadingModelID = SHADINGMODELID_EYE;
GBuffer.CustomData.x = EncodeSubsurfaceProfile(SubsurfaceProfile).x;
GBuffer.CustomData.w = 1.0f - saturate(GetMaterialCustomData0(MaterialParameters)); // Opacity = 1.0 - Iris Mask
#elif MATERIAL_SHADINGMODEL_STYLIZED_SHADOW
GBuffer.ShadingModelID = SHADINGMODELID_STYLIZED_SHADOW;
GBuffer.CustomData.x = GetMaterialCustomData0(MaterialParameters);
#if IRIS_NORMAL
BasePassCommon.ush
//40
// Only some shader models actually need custom data.
#define WRITES_CUSTOMDATA_TO_GBUFFER (USES_GBUFFER && (MATERIAL_SHADINGMODEL_SUBSURFACE || MATERIAL_SHADINGMODEL_PREINTEGRATED_SKIN || MATERIAL_SHADINGMODEL_SUBSURFACE_PROFILE || MATERIAL_SHADINGMODEL_CLEAR_COAT || MATERIAL_SHADINGMODEL_TWOSIDED_FOLIAGE || MATERIAL_SHADINGMODEL_HAIR || MATERIAL_SHADINGMODEL_CLOTH || MATERIAL_SHADINGMODEL_EYE || MATERIAL_SHADINGMODEL_STYLIZED_SHADOW ))
5. 改变衰减计算
DeferredLightingCommon.ush
//256
float4 GetDynamicLighting(float3 WorldPosition, float3 CameraVector, FGBufferData GBuffer, float AmbientOcclusion, uint ShadingModelID, FDeferredLightData LightData, float4 LightAttenuation, float Dither, uint2 SVPos)
虚幻使用以下计算来确定最终光倍数:LightColor (NoL SurfaceAttenuation)
我们将创建一个新的光衰减变量 。
float3 AttenuationColor = 0.f;
BRANCH
if(ShadingModelID == SHADINGMODELID_STYLIZED_SHADOW)
{
float Range = GBuffer.CustomData.x * 0.5f;
AttenuationColor = LightColor *((DistanceAttenuation * LightRadiusMask * SpotFalloff)* smoothstep(0.5f - Range,0.5f + Range,SurfaceShadow)* 0.1f);
}
else
{
AttenuationColor = LightColor *(NoL * SurfaceAttenuation);
}
新的着色系统 ,改了 阴影的计算 ,我们先不用这一套 。
6. 更改表面着色
ShadingModels.ush
//910
FDirectLighting IntegrateBxDF( FGBufferData GBuffer, half3 N, half3 V, half3 L, float Falloff, float NoL, FAreaLight AreaLight, FShadowTerms Shadow )
switch(GBuffer.ShadingModelID)
{
...
case SHADINGMODELID_EYE:
return EyeBxDF(GBuffer,N,V,L,Falloff,NoL,AreaLight,Shadow);
case SHADINGMODELID_STYLIZED_SHADOW:
return StylizedShadowShading(GBuffer,N,V,L,Falloff,NoL,AreaLight,Shadow);
default:
return (FDirectLighting)0;
}
然后我们来定义这个 StylizedShadowShading 函数
FDirectLighting StylizedShadowShading(FGBufferData GBuffer, half3 N, half3 V, half3 L, float Falloff, float NoL, FAreaLight AreaLight, FShadowTerms Shadow)
{
float Range = GBuffer.CustomData.x * 0.5f;
float3 H = normalize(V+L);
float NoH = saturate( dot(N, H));
FDirectLighting Lighting ;
Lighting.Diffuse = AreaLight.FalloffColor * (Falloff * NoL) * Diffuse_Lambert( GBuffer.DiffuseColor );
Lighting.Specular = saturate( smoothstep( 0.5f-Range , 0.5f + Range , D_GGX(GBuffer.Roughness, NoH)) * GBuffer.SpecularColor );
Lighting.Transmission = 0 ;
return Lighting ;
}
7. 支持半透明
BasePassPixelShader.usf
//1030
// Volume lighting for lit translucency
// Volume lighting for lit translucency
// #if (MATERIAL_SHADINGMODEL_DEFAULT_LIT || MATERIAL_SHADINGMODEL_SUBSURFACE) && (MATERIALBLENDING_TRANSLUCENT || MATERIALBLENDING_ADDITIVE) && !SIMPLE_FORWARD_SHADING && !FORWARD_SHADING
#if (MATERIAL_SHADINGMODEL_DEFAULT_LIT || MATERIAL_SHADINGMODEL_SUBSURFACE || MATERIAL_SHADINGMODEL_STYLIZED_SHADOW ) && (MATERIALBLENDING_TRANSLUCENT || MATERIALBLENDING_ADDITIVE) && !SIMPLE_FORWARD_SHADING && !FORWARD_SHADING
Color += GetTranslucencyVolumeLighting(MaterialParameters, PixelMaterialInputs, BasePassInterpolants, GBuffer, IndirectIrradiance);
#endif
8. 自定义接口名称
MaterialGraph.cpp
// 527
FText UMaterialGraph::GetCustomDataPinName( uint32 Index ) const
在 index == 0 的那个条件下 ,默认为custom data 0 , 我们可以更改他的显示名称:
if( Index == 0 )
{
switch( Material->GetShadingModel() )
{
...
case MSM_Eye:
return LOCTEXT("IrisMask", "Iris Mask");
case MSM_StylizedShadow :
return LOCTEXT("StylizedShadow", "StylizedShadow");
default:
return LOCTEXT("CustomData0", "Custom Data 0");
}
}